﻿
Imports System.Runtime.CompilerServices
Imports System.Net.Sockets
Imports System.IO
Imports osi.root.envs
Imports osi.root.constants
Imports osi.root.connector
Imports osi.root.utils
Imports osi.root.formation
Imports osi.root.procedure
Imports osi.service.tcp.constants

#Const SOCKET_RECEIVE = True
#Const SOCKET_SEND = True
#Const POLL_READ_SELECT = False

Public Module _extension
    Private ReadOnly empty_buff() As Byte

    Public Function use_socket_send() As Boolean
#If SOCKET_SEND Then
        Return True
#Else
        Return False
#End If
    End Function

    Public Function use_socket_receive() As Boolean
#If SOCKET_RECEIVE Then
        Return True
#Else
        Return False
#End If
    End Function

    Sub New()
        ReDim empty_buff(0)
        assert(npos < 0)
    End Sub

    Private Function operate_timeout_ms(ByVal rate_sec As Int32,
                                        ByVal count As Int64,
                                        ByVal response_timeout_ms As Int64) As Int64
        If response_timeout_ms < 0 Then
            Return npos
        Else
            Dim r As Decimal = 0
            r = rate_to_ms(rate_sec, count)
            r += response_timeout_ms
            If r > max_int64 Then
                Return npos
            Else
                Return CLng(r)
            End If
        End If
    End Function

    <Extension()> Public Sub shutdown(ByVal client As TcpClient)
        On Error Resume Next
        If Not client Is Nothing Then
            client.GetStream().Close()
            client.GetStream().Dispose()
            client.Client().Shutdown(SocketShutdown.Both)
            'client.Client().SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.DontLinger, False)
            'client.Client().Close(0)
            client.Client().Close()
            client.Close()
        End If
    End Sub

    <Extension()> Public Function send_buff_size(ByVal c As TcpClient) As Int32
        If c Is Nothing OrElse
           c.Client() Is Nothing Then
            Return failure_send_buff_size
        Else
            Return c.Client().send_buff_size()
        End If
    End Function

    <Extension()> Public Function receive_buff_size(ByVal c As TcpClient) As Int32
        If c Is Nothing OrElse
           c.Client() Is Nothing Then
            Return failure_receive_buff_size
        Else
            Return c.Client().receive_buff_size()
        End If
    End Function

    <Extension()> Public Function no_delay(ByVal c As TcpClient) As Boolean
        If c Is Nothing OrElse
           c.Client() Is Nothing Then
            Return False
        Else
            Return c.Client().no_Delay()
        End If
    End Function

    <Extension()> Public Function set_no_delay(ByVal c As TcpClient, ByVal v As Boolean) As Boolean
        If c Is Nothing OrElse
           c.Client() Is Nothing Then
            Return False
        Else
            Return c.Client().set_no_delay(v)
        End If
    End Function

    Private Function operate(ByVal c As TcpClient,
                             ByVal timeout_ms As Int64,
                             ByVal s As Func(Of NetworkStream, event_comb)) As event_comb
        assert(Not s Is Nothing)
        Dim ec As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  If alive(c) Then
                                      ec = s(c.GetStream())
                                      Return waitfor(ec, timeout_ms) AndAlso
                                             goto_next()
                                  Else
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() Then
                                      Return goto_end()
                                  Else
                                      shutdown(c)
                                      Return False
                                  End If
                                  Return ec.end_result() AndAlso
                                         goto_end()
                              End Function)
    End Function

    <Extension()> Public Function send(ByVal c As TcpClient,
                                       ByVal i As Stream,
                                       ByVal count As Int64,
                                       ByVal buff_size As Int32,
                                       ByVal rate_sec As Int32,
                                       Optional ByVal result As pointer(Of Int64) = Nothing) As event_comb
        Return operate(c,
                       rate_to_ms(rate_sec, count),
                       Function(os) copy_to(i,
                                            os,
                                            count,
                                            buff_size,
                                            rate_to_ms(rate_sec, buff_size),
                                            result))
    End Function

    <Extension()> Public Function send(ByVal c As TcpClient,
                                       ByVal i As Stream,
                                       ByVal count As Int64,
                                       ByVal rate_sec As Int32,
                                       Optional ByVal result As pointer(Of Int64) = Nothing) As event_comb
        Return send(c, i, count, default_value.buff_size, rate_sec, result)
    End Function

    <Extension()> Public Function send(ByVal c As TcpClient,
                                       ByVal i As Stream,
                                       ByVal rate_sec As Int32,
                                       Optional ByVal result As pointer(Of Int64) = Nothing) As event_comb
        Return send(c, i, npos, rate_sec, result)
    End Function

    <Extension()> Public Function send(ByVal c As TcpClient,
                                       ByVal i As Stream,
                                       Optional ByVal result As pointer(Of Int64) = Nothing) As event_comb
        Return send(c, i, default_value.send_rate_sec, result)
    End Function

    <Extension()> Public Function send(ByVal client As TcpClient,
                                       ByVal buff() As Byte,
                                       ByVal offset As Int32,
                                       ByVal count As Int32,
                                       ByVal rate_sec As Int32) As event_comb
#If SOCKET_SEND Then
        Dim ec As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  If alive(client) Then
                                      ec = client.Client().send(buff, offset, count, rate_sec)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  Else
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() Then
                                      Return goto_end()
                                  Else
                                      shutdown(client)
                                      Return False
                                  End If
                              End Function)
#Else
        Return operate(client,
                       rate_to_ms(rate_sec, count),
                       Function(s) write(s, buff, offset, count))
#End If
    End Function

    <Extension()> Public Function send(ByVal client As TcpClient,
                                       ByVal buff() As Byte,
                                       ByVal count As Int32,
                                       ByVal rate_sec As Int32) As event_comb
        Return send(client, buff, 0, count, rate_sec)
    End Function

    <Extension()> Public Function send(ByVal client As TcpClient,
                                       ByVal buff() As Byte,
                                       ByVal rate_sec As Int32) As event_comb
        Return send(client, buff, array_size(buff), rate_sec)
    End Function

    <Extension()> Public Function send(ByVal client As TcpClient,
                                       ByVal buff() As Byte) As event_comb
        Return send(client, buff, default_value.send_rate_sec)
    End Function

    <Extension()> Public Function receive(ByVal c As TcpClient,
                                          ByVal o As Stream,
                                          ByVal count As Int64,
                                          ByVal buff_size As Int32,
                                          ByVal response_timeout_ms As Int64,
                                          ByVal rate_sec As Int32,
                                          Optional ByVal result As pointer(Of Int64) = Nothing) As event_comb
        Return operate(c,
                       operate_timeout_ms(rate_sec, count, response_timeout_ms),
                       Function(s As Stream) As event_comb
                           Dim ec As event_comb = Nothing
                           Return New event_comb(Function() As Boolean
                                                     ec = poll_read(c, response_timeout_ms)
                                                     Return waitfor(ec) AndAlso
                                                            goto_next()
                                                 End Function,
                                                 Function() As Boolean
                                                     If ec.end_result() Then
                                                         ec = copy_to(s,
                                                                      o,
                                                                      count,
                                                                      buff_size,
                                                                      rate_to_ms(rate_sec, min(buff_size, count)),
                                                                      result)
                                                         Return waitfor(ec) AndAlso
                                                                goto_next()
                                                     Else
                                                         Return False
                                                     End If
                                                 End Function,
                                                 Function() As Boolean
                                                     Return ec.end_result() AndAlso
                                                            goto_end()
                                                 End Function)
                       End Function)
    End Function

    <Extension()> Public Function receive(ByVal c As TcpClient,
                                          ByVal o As Stream,
                                          ByVal count As Int64,
                                          ByVal response_timeout_ms As Int64,
                                          ByVal rate_sec As Int32,
                                          Optional ByVal result As pointer(Of Int64) = Nothing) As event_comb
        Return receive(c, o, count, default_value.buff_size, response_timeout_ms, rate_sec, result)
    End Function

    <Extension()> Public Function receive(ByVal c As TcpClient,
                                          ByVal o As Stream,
                                          ByVal response_timeout_ms As Int64,
                                          ByVal rate_sec As Int32,
                                          Optional ByVal result As pointer(Of Int64) = Nothing) As event_comb
        Return receive(c, o, npos, response_timeout_ms, rate_sec, result)
    End Function

    <Extension()> Public Function receive(ByVal c As TcpClient,
                                          ByVal o As Stream,
                                          ByVal response_timeout_ms As Int64,
                                          Optional ByVal result As pointer(Of Int64) = Nothing) As event_comb
        Return receive(c, o, response_timeout_ms, default_value.receive_rate_sec, result)
    End Function

    <Extension()> Public Function receive(ByVal c As TcpClient,
                                          ByVal o As Stream,
                                          ByVal rate_sec As Int32,
                                          Optional ByVal result As pointer(Of Int64) = Nothing) As event_comb
        Return receive(c, o, npos, rate_sec, result)
    End Function

    <Extension()> Public Function receive(ByVal c As TcpClient,
                                          ByVal o As Stream,
                                          Optional ByVal result As pointer(Of Int64) = Nothing) As event_comb
        Return receive(c, o, default_value.receive_rate_sec, result)
    End Function

#If SOCKET_RECEIVE Then
    <Extension()> Public Function partial_receive(ByVal client As TcpClient,
                                                  ByVal buff() As Byte,
                                                  ByVal offset As Int32,
                                                  ByVal count As Int32,
                                                  ByVal response_timeout_ms As Int64,
                                                  ByVal rate_sec As Int32,
                                                  Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Dim ec As event_comb = Nothing
        Dim bdl As pointer(Of Int32) = Nothing
        Return New event_comb(Function() As Boolean
                                  bdl = New pointer(Of Int32)()
                                  ec = poll_read(client, response_timeout_ms, bdl)
                                  Return waitfor(ec) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() Then
                                      If (+bdl) > 0 Then
                                          ec = client.Client().partial_receive(buff, offset, count, rate_sec, result)
                                          Return waitfor(ec) AndAlso
                                                 goto_next()
                                      Else
                                          Return eva(result, 0) AndAlso
                                                 goto_end()
                                      End If
                                  Else
                                      shutdown(client)
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() Then
                                      Return goto_end()
                                  Else
                                      shutdown(client)
                                      Return False
                                  End If
                              End Function)
    End Function

    <Extension()> Public Function partial_receive(ByVal client As TcpClient,
                                                  ByVal buff() As Byte,
                                                  ByVal count As Int32,
                                                  ByVal response_timeout_ms As Int64,
                                                  ByVal rate_sec As Int32,
                                                  Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Return partial_receive(client, buff, 0, count, response_timeout_ms, rate_sec, result)
    End Function

    <Extension()> Public Function partial_receive(ByVal client As TcpClient,
                                                  ByVal buff() As Byte,
                                                  ByVal response_timeout_ms As Int64,
                                                  ByVal rate_sec As Int32,
                                                  Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Return partial_receive(client, buff, array_size(buff), response_timeout_ms, rate_sec, result)
    End Function

    <Extension()> Public Function partial_receive(ByVal client As TcpClient,
                                                  ByVal buff() As Byte,
                                                  ByVal response_timeout_ms As Int64,
                                                  Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Return partial_receive(client, buff, response_timeout_ms, default_value.receive_rate_sec, result)
    End Function

    <Extension()> Public Function partial_receive(ByVal client As TcpClient,
                                                  ByVal buff() As Byte,
                                                  ByVal rate_sec As Int32,
                                                  Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Return partial_receive(client, buff, default_value.response_timeout_ms, rate_sec, result)
    End Function

    <Extension()> Public Function partial_receive(ByVal client As TcpClient,
                                                  ByVal buff() As Byte,
                                                  Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Return partial_receive(client, buff, default_value.receive_rate_sec, result)
    End Function
#End If

    <Extension()> Public Function stream_receive(ByVal c As TcpClient,
                                                 ByVal buff() As Byte,
                                                 ByVal offset As Int32,
                                                 ByVal count As Int32,
                                                 ByVal response_timeout_ms As Int64,
                                                 ByVal rate_sec As Int32,
                                                 Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Return operate(c,
                       operate_timeout_ms(rate_sec, count, response_timeout_ms),
                       Function(s As Stream) As event_comb
                           Dim ec As event_comb = Nothing
                           Return New event_comb(Function() As Boolean
                                                     ec = poll_read(c, response_timeout_ms)
                                                     Return waitfor(ec) AndAlso
                                                            goto_next()
                                                 End Function,
                                                 Function() As Boolean
                                                     If ec.end_result() Then
                                                         ec = read(s, buff, offset, count, result)
                                                         Return waitfor(ec) AndAlso
                                                                goto_next()
                                                     Else
                                                         Return False
                                                     End If
                                                 End Function,
                                                 Function() As Boolean
                                                     Return ec.end_result() AndAlso
                                                            goto_end()
                                                 End Function)
                       End Function)
    End Function

    <Extension()> Public Function socket_receive(ByVal c As TcpClient,
                                                 ByVal buff() As Byte,
                                                 ByVal offset As Int32,
                                                 ByVal count As Int32,
                                                 ByVal response_timeout_ms As Int64,
                                                 ByVal rate_sec As Int32,
                                                 Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Dim ec As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  ec = poll_read(c, response_timeout_ms)
                                  Return waitfor(ec) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() Then
                                      ec = c.Client().receive(buff, offset, count, rate_sec, result)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  Else
                                      shutdown(c)
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() Then
                                      Return goto_end()
                                  Else
                                      shutdown(c)
                                      Return False
                                  End If
                              End Function)
    End Function

    <Extension()> Public Function receive(ByVal client As TcpClient,
                                          ByVal buff() As Byte,
                                          ByVal offset As Int32,
                                          ByVal count As Int32,
                                          ByVal response_timeout_ms As Int64,
                                          ByVal rate_sec As Int32,
                                          Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
#If SOCKET_RECEIVE Then
        Return socket_receive(client, buff, offset, count, response_timeout_ms, rate_sec, result)
#Else
        Return stream_receive(client, buff, offset, count, response_timeout_ms, rate_sec, result)
#End If
    End Function

    <Extension()> Public Function receive(ByVal client As TcpClient,
                                          ByVal buff() As Byte,
                                          ByVal count As Int32,
                                          ByVal response_timeout_ms As Int64,
                                          ByVal rate_sec As Int32,
                                          Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Return receive(client, buff, 0, count, response_timeout_ms, rate_sec, result)
    End Function

    <Extension()> Public Function receive(ByVal client As TcpClient,
                                          ByVal buff() As Byte,
                                          ByVal response_timeout_ms As Int64,
                                          ByVal rate_sec As Int32,
                                          Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Return receive(client, buff, array_size(buff), response_timeout_ms, rate_sec, result)
    End Function

    <Extension()> Public Function receive(ByVal client As TcpClient,
                                          ByVal buff() As Byte,
                                          ByVal response_timeout_ms As Int64,
                                          Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Return receive(client, buff, response_timeout_ms, default_value.receive_rate_sec, result)
    End Function

    <Extension()> Public Function receive(ByVal client As TcpClient,
                                          ByVal buff() As Byte,
                                          ByVal rate_sec As Int32,
                                          Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Return receive(client, buff, default_value.response_timeout_ms, rate_sec, result)
    End Function

    <Extension()> Public Function receive(ByVal client As TcpClient,
                                          ByVal buff() As Byte,
                                          Optional ByVal result As pointer(Of Int32) = Nothing) As event_comb
        Return receive(client, buff, default_value.receive_rate_sec, result)
    End Function

    <Extension()> Public Function free_poll_alive(ByVal client As TcpClient,
                                                  Optional ByVal p As powerpoint = Nothing) As Boolean
        If poll_alive(client) Then
            Return True
        Else
            If tcp_trace Then
                raise_error("shutdown a connection ",
                            If(p Is Nothing, identity(client), p.identity()),
                            " because of connectivity")
            End If
            shutdown(client)
            Return False
        End If
    End Function

#If 0 Then
    Private Function acceptable_socket_exception(ByVal ex As SocketException) As Boolean
        Return assert(Not ex Is Nothing) AndAlso
               ex.SocketErrorCode() = SocketError.WouldBlock
        Return assert(Not ex Is Nothing) AndAlso
               (ex.SocketErrorCode() = SocketError.WouldBlock OrElse
                ex.SocketErrorCode() = SocketError.TimedOut)
    End Function
#End If

    <Extension()> Public Function poll_alive(ByVal client As TcpClient) As Boolean
        Try
            If client Is Nothing OrElse
               Not client.Connected() OrElse
               client.Client() Is Nothing OrElse
               Not client.Client().Connected() Then
                If tcp_trace Then
                    raise_error("client is not connected, treat as dead")
                End If
                Return False
            Else
                If client.Client().poll_error() Then
                    If tcp_trace Then
                        raise_error("polled error from client, treat as dead")
                    End If
                    Return False
                ElseIf client.Client().poll_read() Then
                    Dim r As Boolean = False
                    Try
                        r = (client.Client().receive(empty_buff, 1, SocketFlags.Peek) = 1)
                    Catch ex As SocketException
                        r = False
                    End Try
                    If r AndAlso
                       client.Connected() Then
                        Return True
                    Else
                        If tcp_trace AndAlso Not r Then
                            raise_error("polled read, but cannot receive from client, treat as dead")
                        End If
                        Return False
                    End If
                ElseIf client.Client().poll_write() Then
                    Dim ob As Boolean = False
                    Dim c As Socket = Nothing
                    Dim r As Boolean = False
                    c = client.Client()
                    ob = c.Blocking()
                    Try
                        c.Blocking() = False
                        r = (c.send(empty_buff, 0, SocketFlags.None) = 0)
                    Catch ex As SocketException
                        r = False
                    Finally
                        c.Blocking() = ob
                    End Try
                    If r AndAlso
                       client.Connected() AndAlso
                       c.Connected() Then
                        Return True
                    Else
                        If tcp_trace Then
                            raise_error("failed to send unblock data to the client, treat as dead")
                        End If
                        Return False
                    End If
                Else
                    Return True
                End If
                'Return client.GetStream().can_read() AndAlso
                '       client.GetStream().can_write() AndAlso
                '       client.GetStream().data_available() <> ternary.unknown
                'Return client.Client().can_send() AndAlso
                '       client.Client().can_receive() AndAlso
                '       client.Client().data_buffered() <> ternary.unknown
            End If
            'Return operate(client, maxInt64, Function(x) x.can_write())
        Catch ex As Exception
            raise_error("caught unhandled exception when poll client status, treat as dead, ex ",
                        ex.Message,
                        ", callstack ",
                        ex.StackTrace())
            Return False
        End Try
    End Function

    <Extension()> Public Function alive(ByVal client As TcpClient) As Boolean
        If client Is Nothing OrElse
           client.Client() Is Nothing Then
            Return False
        Else
            Try
                Return client.Connected()
            Catch
                Return False
            End Try
        End If
    End Function

    '=0 no data to read
    '<0 client is not connected
    '>0 has data to read
    <Extension()> Public Function buffered_bytes(ByVal client As TcpClient) As Int32
        If alive(client) Then
            Try
                Return client.Available()
            Catch
                Return npos
            End Try
        Else
            Return npos
        End If
    End Function

    <Extension()> Public Function identity(ByVal c As TcpClient) As String
        If c Is Nothing OrElse
           c.Client() Is Nothing Then
            Return "disconnected"
        Else
            Try
                Return strcat(c.Client().LocalEndPoint(),
                              "-",
                              c.Client().RemoteEndPoint())
            Catch
                Return "broken"
            End Try
        End If
    End Function

    'return true if there is data to read and client is still alive
    'return false if the client is dead
    <Extension()> Public Function poll_read(ByVal c As TcpClient) As event_comb
        Return poll_read(c, npos, Nothing)
    End Function

    'return false if no data came during the timeout_ms range or client is dead
    <Extension()> Public Function poll_read(ByVal c As TcpClient,
                                            ByVal timeout_ms As Int64) As event_comb
        Dim ec As event_comb = Nothing
        Dim bdl As pointer(Of Int32) = Nothing
        Return New event_comb(Function() As Boolean
                                  bdl = New pointer(Of Int32)()
                                  ec = poll_read(c, timeout_ms, bdl)
                                  Return waitfor(ec) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         (+bdl) > 0 AndAlso
                                         goto_end()
                              End Function)
    End Function

    <Extension()> Public Function poll_read(ByVal c As TcpClient,
                                            ByVal timeout_ms As Int64,
                                            ByVal buffered_data_len As pointer(Of Int32)) As event_comb
#If POLL_READ_SELECT Then
        Return New event_comb(Function() As Boolean
                                  Return waitfor(poll_read_select.queue(c, timeout_ms)) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  Dim v As Int32 = 0
                                  v = buffered_bytes(c)
                                  Return v >= 0 AndAlso
                                         eva(buffered_data_len, v) AndAlso
                                         goto_end()
                              End Function)
#Else
        Dim lbb As Int32 = 0
        Return New event_comb(Function() As Boolean
                                  If alive(c) Then
                                      If timeout_ms = 0 Then
                                          lbb = buffered_bytes(c)
                                          Return goto_next()
                                      ElseIf timeout_ms < 0 Then
                                          timeout_ms = max_int64
                                      End If
                                      Dim sc As Int64 = 0
                                      Dim lc As Int64 = 0
                                      sc = nowadays.milliseconds()
                                      Return waitfor(Function() As Boolean
                                                         Const wc As Int64 = constants.interval_ms.watch_connectivity
                                                         Dim mc As Int64 = 0
                                                         mc = nowadays.milliseconds()
                                                         lbb = buffered_bytes(c)
                                                         If lbb <> 0 OrElse
                                                            mc - sc >= timeout_ms Then
                                                             Return True
                                                         Else
                                                             If mc - lc >= wc Then
                                                                 If Not free_poll_alive(c) Then
                                                                     Return True
                                                                 End If
                                                                 lc = mc
                                                             End If
                                                             Return False
                                                         End If
                                                     End Function) AndAlso
                                             goto_next()
                                  Else
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  Return lbb >= 0 AndAlso
                                         eva(buffered_data_len, lbb) AndAlso
                                         goto_end()
                              End Function)

#End If
    End Function

    <Extension()> Public Function handle_id(ByVal this As TcpClient) As Int64
        If this Is Nothing Then
            Return npos
        Else
            Return this.Client().handle_id()
        End If
    End Function

    <Extension()> Public Function handle_id(ByVal this As Socket) As Int64
        If this Is Nothing Then
            Return npos
        Else
            Return this.Handle().ToInt64()
        End If
    End Function

    <Extension()> Public Function set_keepalive(ByVal c As TcpClient,
                                                Optional ByVal enable As Boolean = _
                                                    constants.default_value.enable_keepalive,
                                                Optional ByVal first_keepalive_ms As UInt32 = _
                                                    constants.default_value.first_keepalive_ms,
                                                Optional ByVal interval_ms As UInt32 = _
                                                    constants.default_value.keepalive_interval_ms) As Boolean
        If c Is Nothing Then
            Return False
        Else
            Try
                c.Client().SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, CInt(True))
            Catch ex As Exception
                If tcp_trace Then
                    raise_error(error_type.warning, "failed to set socket option keepalive, ex ", ex.Message())
                End If
                Return False
            End Try
            Dim v() As Byte = Nothing
            ReDim v(sizeof_uint32 * 3 - 1)
            Dim offset As UInt32 = 0
            If enable Then
                assert(uint32_bytes(uint32_1, v, offset))
            Else
                assert(uint32_bytes(uint32_0, v, offset))
            End If
            assert(uint32_bytes(first_keepalive_ms, v, offset))
            assert(uint32_bytes(interval_ms, v, offset))
            assert(offset = array_size(v))
            Dim r As Int32 = 0
            Try
                r = c.Client().IOControl(IOControlCode.KeepAliveValues, v, Nothing)
            Catch ex As Exception
                If tcp_trace Then
                    raise_error(error_type.warning, "failed to set io control keepalive values, ex ", ex.Message())
                End If
                Return False
            End Try
            If tcp_trace Then
                raise_error("return value of io control keepalive values is ", r)
            End If
            Return True
        End If
    End Function

    <Extension()> Public Function enable_keepalive(ByVal c As TcpClient,
                                                   Optional ByVal first_keepalive_ms As UInt32 = _
                                                       constants.default_value.first_keepalive_ms,
                                                   Optional ByVal interval_ms As UInt32 = _
                                                       constants.default_value.keepalive_interval_ms) As Boolean
        Return set_keepalive(c, first_keepalive_ms, interval_ms)
    End Function
End Module
